iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Modern Web

如何用TypeScript水30天鐵人賽系列 第 19

[Day19]:紅燈停綠燈行 - 型別檢測&Narrowing

  • 分享至 

  • xImage
  •  

Day 19 Banner

紅燈停綠燈行

紅燈停、綠燈行,那黃燈呢?
就讓Type Guard(型別檢測)告訴你吧~
─────────────────────────── By Opshell


目標:Type Guard(型別檢測)

在JavaScript中其實也是有型別的,
只是他很善變,在不知道發生什麼事的情況下,
很容易就臉盲了,套一句某客戶對我說的話:
你這樣很不穩定ㄟ (吐血)


過程:

Type Guard(型別檢測)
他的工作是在執行時,Narrowing(限縮型別)的表達式,
是使用各種方式判斷、轉換型別,來導流處理,
避免TS在compile(編譯)時報錯。
下面是常見的幾種方式:

  • 使用 JS 運算子:typeofinstanceofisArray
  • 使用使用者自定義的型別檢查:型別謂語(type predicates)in 關鍵字

  • 1. typeof

    typeof用來檢測未經計算的資料型別。
    ※ 在使用 typeof運算子時,有幾個型別要特別小心,
    分別是原始型別的null以及Array,兩者判斷後的結果都是物件。

     // 讓我們利用"型別檢測"來修改之前的 getAgeLength function
     // 之前利用(<string>.age)的方式來處理,
     // 現在改成透過型別檢測判斷是否為字串,進行導流
     // 在某方面來說,可讀性更好。
     function getAgeLength(age: string | number): number {
         let result = 0;
         if (typeof age === 'string') {
             result = age.length;
         } else {
             result = age.toString().length;
         }
    
         return result;
     }
    

  • 2. instanceof

    instanceof是用來判斷A是否為B的實例,比較的是原型(prototype)
    換句話說,就是有沒有在原型鏈上面

     // 建立Member Class 設定 名稱、年紀、性別為建構子
     class Member {
         constructor(public name: string, public year: number, public gender: string) { }
     }
     // 建立Team Class 團隊名稱、團隊成員 性別為建構子
     // 這邊可以看出 members建構子的型別是 Member Class
     class Team {
         constructor(public title: string, public members: Member[]) { }
     }
    
     function getName(cls: Member | Team) {
         return (cls instanceof Member) ? cls.name : cls.title;
     }
     const how = new Member('Opshell', 30, 'man');
     const zoo = new Team('Maya', [how]);
    
     console.log(getName(how)); // Opshell
     console.log(getName(zoo)); // 因為 Team 不在 Member 的原型鏈上面,所以印出 Maya
    

    ※ instanceof只能判斷兩個比較對象是否屬於實例關係,無法明確指出具體屬於哪種類型。


  • 3. ParameterName is Type(型別謂語):

    是一個具特殊回傳值的函式,
    它會向編譯器傳遞訊號,告知回傳值的型別。
    ParameterName is Type(型別謂語)只能用在單一傳入值,且會回傳布林值。
    下面的範例中型別謂語也就是 s is string,語法為參數名 is 型別

     type tMan = 'xy';
     type tWoman = 'xx';
    
     function isMan(gender: tMan | tWoman): gender is tMan {
         return (gender as tMan) === 'xy';
     }
    
     /** 如果gender 沒有使用"斷言"指定型別,
      * 他會被"推論"成字串
      * 這邊就是前面提到的,用"斷言"覆蓋"推論"的做法
      * 無法丟進去isMan做驗證喔
      */
     const Opshell = {
         gender: (<tMan>'xy'),
         year: 30
     };
    
     console.log(isMan(Opshell.gender)); // true
    

  • 4. in 關鍵字:

    使用 in 關鍵字會回傳布林值,
    判斷屬性是否存在特定物件或其原型鏈上,
    語法為 n in x,n 為字串或字串字面值,x 為 union型別

     class Member {
         constructor(public name: string, public year: number, public gender: string) { }
     }
     class Team {
         constructor(public title: string, public members: Member[]) { }
     }
    
     function isMember(cls: any): cls is Member {
         return 'name' in cls;
     }
    
     const Opshell = new Member('Opshell', 30, 'man');
     const Zoo = new Team('Maya', [Opshell]);
    
     console.log(isMember(Opshell)); // true
     console.log(isMember(Zoo)); // false
    

小結:

看完上面的例子可以發現Type Guard(型別檢測)
跟我們平常在Js中會做的事情差不多,
判斷型別決定要做的事情,
只是TS提供更多樣、嚴謹的方式來幫助 ~~(強迫)~~你做。
讓程式碼更嚴謹易讀,上面只是一些比較常見的方式,
還有更多厲害的看看延伸閱讀其他大大寫的文章,
在後續的學習中,也會看到更多樣的型別檢測(Type Guard)方式,
大家明天見囉~


延伸閱讀:

  1. Typescript 一些令人又愛又恨的內容 — Type Guard、Narrowing -> 延伸到泛型 - By Andy Chen
  2. 【TS】Type Guard and Narrowing - By pjchender

上一篇
[Day18]:小鴨鴨排隊游 - Enum列舉
下一篇
[Day20]:別人的輪子用起來 - 宣告檔案 Part 1
系列文
如何用TypeScript水30天鐵人賽33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言